home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Atari Mega Archive 1
/
Atari Mega Archive - Volume 1.iso
/
mint
/
lib
/
mntlib44.zoo
/
mntlib
/
gmon.c
< prev
next >
Wrap
C/C++ Source or Header
|
1993-05-23
|
18KB
|
700 lines
/*
* monitor(3), mcount() and profil(2) clones for gcc-Tos library
*
* Note: these routines need tuning up. they are very space
* inefficient. the implementation is totally biased towards support
* for gprof rather than prof (does anyone use prof anymore? why?)
*
* ++jrb bammi@cadence.com
*/
#include <stddef.h>
#include <memory.h>
#include <unistd.h>
#include <fcntl.h>
#include <assert.h>
#include <osbind.h>
#include <basepage.h>
#include <sysvars.h>
#include <xbra.h>
#include <string.h>
#ifndef _COMPILER_H
#include <compiler.h>
#endif
/* gmon header */
struct gm_header {
void *low; /* low pc */
void *high; /* hi pc */
unsigned long nbytes; /* bytes in header + histo size */
};
typedef unsigned short CHUNK; /* type of each histogram entry */
struct gm_call { /* gm call record */
void *from; /* the caller */
void *to; /* the called function (callee) */
unsigned long ncalls; /* # of calls from FROM to TO */
};
#define GMON_FILE "gmon.out" /* name of GMON file */
/* format of gmon file
* gm_header
* ((gm_header.nbytes - sizeof(gm_header))/sizeof(CHUNK)) histo entries
* gm_call records upto EOF
*/
/* histogram variables and defines */
#define HIST_SCALE 2 /* text space scaled into size/HIST_SCALE CHUNKS */
#define HIST_SHIFT 1 /* HIST_SCALE == 2 ** HIST_SHIFT (assumption) */
/* 1 <= HIST_SHIFT <= 8 (assumption) */
static CHUNK *hist_buffer; /* histogram buffer */
static unsigned long hist_size; /* size of histogram in bytes */
/* call graph variables and defines */
typedef struct { /* a to chain element */
void *selfpc; /* the callee's pc */
unsigned long count; /* number of times called */
unsigned short link; /* link to next in chain (an index) */
} tostruct ;
tostruct *tos; /* pool of to chain elements */
unsigned short *froms; /* called from hash chain heads (an index) */
/* inherent assumption: typeof froms == typeof CHUNK, otherwise
change code in monstartup() */
#define MINARCS 64 /* min # of to's, a rand() # */
#define ARCDENSITY 2 /* scaling of to's (as a % of textsize) */
#define HASHFRACTION 1 /* scaling of froms over textsize.
note this is very memory wasteful,
but the alternatives are worse
beacuse of two reasons:
- increase compute requirements(in mcount)
- bsr, followed by bsr will loose!
the coding of mcount below almost
assumes that HASHFRACTION==1
anyone else have some brilliant ideas?
*/
#define HASH_SHIFT 0 /* HASHFRACTION = 2 ** HASH_SHIFT (assumption) */
/* housekeeping variables */
static long profiling; /* profiling flag */
static unsigned long textsize; /* size of profiled text area */
static unsigned short tolimit; /* max to == 65534, min == MINARCS */
static unsigned short toalloc; /* next free to record index */
static void *s_lowpc; /* low pc rounded down to multiples
of histo density =
(CHUNK size * HIST_SCALE)
(assumption: its mult of 2)
*/
#define USL(X) ((unsigned long)(X)) /* cast X to unsigned long */
/* round X down to last multiple of Y */ /* see assumption above */
#define ROUNDDOWN(X,Y) ( USL(X) & (~(USL((Y)-1))) )
/* round X up to next multiple of Y */
#define ROUNDUP(X,Y) ( USL((X)+((Y)-1)) & (~(USL((Y)-1))) )
/* functions */
__EXTERN void monstartup __PROTO((void *lowpc, void *highpc));
__EXTERN void monitor __PROTO((void *lowpc, void *highpc, void *buffer,
unsigned long bufsize, unsigned int nfunc));
__EXTERN void moncontrol __PROTO((long flag));
__EXTERN void _mcleanup __PROTO((void));
__EXTERN int profil __PROTO((void *buff, unsigned long bufsiz, unsigned long offset,
int shift));
/* static */ void tick __PROTO((void));
static void term __PROTO((void));
static void install_handlers __PROTO((void));
static void remove_handlers __PROTO((void));
static void unlink_handler __PROTO((xbra_struct *me, int exc));
static void build_graph __PROTO((void *caller, void *callee));
/*
* allocate space for histogram and call graph given the sampling
* range. call monitor to start up profiling.
*/
void monstartup(lowpc, highpc)
void *lowpc, *highpc;
{
unsigned long monsize; /* size of hist buffer + gm_header rounded */
void *buf; /* hist + gm_header space */
unsigned long i;
assert(USL(lowpc) < USL(highpc));
#if 0 /* dont define: screws up gmon because of reloc assumptions */
s_lowpc = lowpc = (void *)
(ROUNDDOWN(USL(lowpc), sizeof(CHUNK)<<HIST_SHIFT ));
#else
s_lowpc = lowpc;
#endif
highpc = (void *)
(ROUNDUP(USL(highpc), sizeof(CHUNK)<<HIST_SHIFT ));
textsize = USL(highpc) - USL(lowpc);
/* allocate histogram buffer + gm_header buffer */
monsize = (textsize >> HIST_SHIFT) * sizeof(CHUNK) +
sizeof(struct gm_header);
monsize = ROUNDUP(monsize, sizeof(short));
if((buf = (CHUNK *)malloc(monsize)) == (CHUNK *)0)
{
Cconws("monitor: No memory for histogram buffer\r\n");
froms = (unsigned short *)0;
tos = (tostruct *)0;
return;
}
bzero(buf, monsize);
/* allocate space for graph data structs */
i = (textsize>>HASH_SHIFT) * sizeof(*froms);
i = ROUNDUP(i, sizeof(long));
if((froms = (unsigned short *)malloc(i)) == (unsigned short *)0)
{
Cconws("monitor: No memory for FROMs\r\n");
free(buf);
tos = (tostruct *)0;
return;
}
bzero(froms, i);
i = textsize * ARCDENSITY / 100;
if( i < MINARCS)
i = MINARCS;
else if ( i > 65534)
i = 65534;
tolimit = (unsigned short)i;
i = ROUNDUP(i*sizeof(tostruct), sizeof(long));
if((tos = (tostruct *)malloc(i)) == (tostruct *)0)
{
Cconws("monitor: No memory for TOs pool\r\n");
free(froms);
free(buf);
froms = (unsigned short *)0;
return;
}
bzero(tos, i);
toalloc = 0; /* index of next available element in TOs pool */
monitor(lowpc, highpc, buf, monsize, (unsigned int)tolimit);
}
/*
* monitor(3) interface to profil(2)
* last arg is silly and not used
*/
void monitor(lowpc, highpc, buffer, bufsize, nfunc)
void *lowpc, *highpc, *buffer;
unsigned long bufsize;
unsigned int nfunc;
{
struct gm_header *hdr;
if(lowpc == 0)
{ /* finished */
moncontrol(0L);
_mcleanup();
return;
}
s_lowpc = lowpc; /* in case user is calling */
/* initialize gm_header */
hdr = (struct gm_header *)buffer;
hdr->low = lowpc;
hdr->high = highpc;
hdr->nbytes = bufsize;
hist_size = bufsize - sizeof(struct gm_header); /* sizof hist buffer */
hist_buffer = (CHUNK *)(USL(buffer) + sizeof(struct gm_header));
/* integ. check, (user can call monitor) */
if((hist_size == 0) ||
(hist_size <
(((USL(highpc) - USL(lowpc))>>HIST_SHIFT)*sizeof(CHUNK))) )
{
return;
}
/* note: difference in scaling semantics from unix */
moncontrol(1L); /* begin */
}
/*
* control profiling
*/
void moncontrol(flag)
long flag;
{
if(flag)
{ /* start */
profil(hist_buffer, hist_size, (unsigned long)s_lowpc, HIST_SHIFT);
profiling = 0;
}
else
{
/* stop */
profil((void *)0, 0L, 0L, 0);
profiling = 3;
}
}
/*
* mcount
* called as a part of the entry prologue of a profiled function.
* the function that calls mcount is the CALLEE, and the function
* that called the CALLEE is the CALLER. mcount grabs the
* address of the CALLEE and the address of the CALLER off the
* stack, and then calls build_graph that incrementally
* constructs the call graphs in the FROMs and TOs structures,
* keeping track of the number of times the CALLER called CALLEE.
* on entry the stack look like:
*
* sp-> | ret address of CALLEE |
* |--------------------------|
* . CALLEE's locals .
* .__________________________.
* CALLEEs fp-> | CALLERS saved fp |
* |--------------------------|
* | ret address of CALLER |
* |--------------------------|
*
* Note:
* -this is true becuase -fomit-frame-pointer and -pg are
* incompatible flags (gcc will say so if you try)
*
* -on the 68k, the address of a long count location is passed in a0
* we dont use this, it was a convention for the old prof stuff.
*/
#if __GNUC__ > 1
void mcount (void